/* Microsoft Reference Implementation for TPM 2.0
 *
 *  The copyright in this software is being made available under the BSD License,
 *  included below. This software may be subject to other third party and
 *  contributor rights, including patent rights, and no such rights are granted
 *  under this license.
 *
 *  Copyright (c) Microsoft Corporation
 *
 *  All rights reserved.
 *
 *  BSD License
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list
 *  of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "Tpm.h"
#include "NV_Write_fp.h"

#if CC_NV_Write  // Conditional expansion of this file

/*
This command writes a value to an area in NV memory that was previously defined by
TPM2_NV_DefineSpace().
Proper authorizations are required for this command as determined by TPMA_NV_PPWRITE;
TPMA_NV_OWNERWRITE; TPMA_NV_AUTHWRITE; and, if TPMA_NV_POLICY_WRITE is SET, the
authPolicy of the NV Index.
If the TPMA_NV_WRITELOCKED attribute of the NV Index is SET, then the TPM shall return
TPM_RC_NV_LOCKED.
NOTE 1 If authorization sessions are present, they are checked before checks to see if writes to the NV
Index are locked.
If nvIndexType is TPM_NT_COUNTER, TPM_NT_BITS or TPM_NT_EXTEND, then the TPM shall return
TPM_RC_ATTRIBUTES

这个命令将一个值写入之前由TPM2_NV_DefineSpace()定义的NV内存区域中。
需要获得此命令的适当授权，具体取决于TPMA_NV_PPWRITE； TPMA_NV_OWNERWRITE；TPMA_NV_AUTHWRITE；
如果TPMA_NV_POLICY_WRITE被设置，还需要获得该NV索引的authPolicy。
如果NV索引的TPMA_NV_WRITELOCKED属性被设置，则TPM应返回TPM_RC_NV_LOCKED。
注意1 如果存在授权会话，则在检查之前检查是否对NV索引进行了锁定。
如果nvIndexType是TPM_NT_COUNTER，TPM_NT_BITS或TPM_NT_EXTEND，则TPM应返回TPM_RC_ATTRIBUTES

If offset and the size field of data add to a value that is greater than the dataSize field of the NV Index
referenced by nvIndex, the TPM shall return an error (TPM_RC_NV_RANGE). The implementation may
return an error (TPM_RC_VALUE) if it performs an additional check and determines that offset is greater
than the dataSize field of the NV Index.
If the TPMA_NV_WRITEALL attribute of the NV Index is SET, then the TPM shall return
TPM_RC_NV_RANGE if the size of the data parameter of the command is not the same as the data field
of the NV Index.
If all checks succeed, the TPM will merge the data.size octets of data.buffer value into the nvIndex→data
starting at nvIndex→data[offset]. If the NV memory is implemented with a technology that has endurance
limitations, the TPM shall check that the merged data is different from the current contents of the NV
Index and only perform a write to NV memory if they differ.
After successful completion of this command, TPMA_NV_WRITTEN for the NV Index will be SET.
NOTE 2 Once SET, TPMA_NV_WRITTEN remains SET until the NV Index is undefined or the NV Index is
cleared.

如果偏移量与数据的大小字段之和大于由nvIndex引用的NV索引的数据大小字段，TPM应返回一个错误（TPM_RC_NV_RANGE）。
如果实现执行额外的检查并确定偏移量大于NV索引的数据大小字段，则该实现可能会返回一个错误（TPM_RC_VALUE）。
如果NV索引的 TPMA_NV_WRITEALL 属性被设置，则如果命令的数据参数的大小与NV索引的数据字段不同，则TPM应返回TPM_RC_NV_RANGE。
如果所有检查都成功，则TPM将从nvIndex→data[offset]开始将data.buffer值的数据.size字节合并到nvIndex→data中。
如果NV存储器使用具有耐久性限制的技术实现，则TPM应检查合并的数据与NV索引的当前内容不同，并且只在它们不同时才执行写入NV存储器的操作。
在此命令成功完成后，NV索引的TPMA_NV_WRITTEN将被设置。
注意2一旦被设置，TPMA_NV_WRITTEN 将保持设置，直到NV索引未定义或NV索引被清除。

*/
/*(See part 3 specification)
// Write to a NV index
*/
//  Return Type: TPM_RC
//      TPM_RC_ATTRIBUTES               Index referenced by 'nvIndex' has either
//                                      TPMA_NV_BITS, TPMA_NV_COUNTER, or
//                                      TPMA_NV_EVENT attribute SET
//      TPM_RC_NV_AUTHORIZATION         the authorization was valid but the
//                                      authorizing entity ('authHandle')
//                                      is not allowed to write to the Index
//                                      referenced by 'nvIndex'
//      TPM_RC_NV_LOCKED                Index referenced by 'nvIndex' is write
//                                      locked
//      TPM_RC_NV_RANGE                 if TPMA_NV_WRITEALL is SET then the write
//                                      is not the size of the Index referenced by
//                                      'nvIndex'; otherwise, the write extends
//                                      beyond the limits of the Index
//
TPM_RC
TPM2_NV_Write(
    NV_Write_In     *in             // IN: input parameter list
    )
{
    NV_INDEX        *nvIndex = NvGetIndexInfo(in->nvIndex, NULL);
    TPMA_NV          attributes = nvIndex->publicArea.attributes;
    TPM_RC           result;

// Input Validation

    // Common access checks, NvWriteAccessCheck() may return TPM_RC_NV_AUTHORIZATION
    // or TPM_RC_NV_LOCKED 
    result = NvWriteAccessChecks(in->authHandle,
                                 in->nvIndex,
                                 attributes);
    if(result != TPM_RC_SUCCESS)
        return result;

    // Bits index, extend index or counter index may not be updated by
    // TPM2_NV_Write
    if(IsNvCounterIndex(attributes)
       || IsNvBitsIndex(attributes)
       || IsNvExtendIndex(attributes))
        return TPM_RC_ATTRIBUTES;

    // Make sure that the offset is not too large
    if(in->offset > nvIndex->publicArea.dataSize)
        return TPM_RCS_VALUE + RC_NV_Write_offset;

    // Make sure that the selection is within the range of the Index
    if(in->data.t.size > (nvIndex->publicArea.dataSize - in->offset))
        return TPM_RC_NV_RANGE;

    // If this index requires a full sized write, make sure that input range is
    // full sized.
    // Note: if the requested size is the same as the Index data size, then offset
    // will have to be zero. Otherwise, the range check above would have failed.
    if(IS_ATTRIBUTE(attributes, TPMA_NV, WRITEALL)
       && in->data.t.size < nvIndex->publicArea.dataSize)   
        return TPM_RC_NV_RANGE;

// Internal Data Update

    // Perform the write.  This called routine will SET the TPMA_NV_WRITTEN
    // attribute if it has not already been SET. If NV isn't available, an error
    // will be returned.
    return NvWriteIndexData(nvIndex, in->offset, in->data.t.size,
                            in->data.t.buffer);
}

#endif // CC_NV_Write